package Buildings.office;

import Interfaces.*;
import Errors.*;
import java.util.Iterator;

public class OfficeBuilding implements Building, java.io.Serializable, Cloneable {

    private note mainOfficeFloor; //голова структуры этажей с офисами

    private class note implements java.io.Serializable {

        public Floor f;
        public boolean last = false;
        private note nextNote = null;
        private note prevNote = null;

        note(Floor f) {
            this.f = f;
        }
        note(Floor f,note next) {
            this.f = f;
            nextNote=next;
        }
    }
    private class myIterator implements Iterator {
        private note mainOfficeFloori=new note(null,mainOfficeFloor);
        public boolean hasNext() {
            boolean answer=false;
            if(mainOfficeFloori.last==false)answer=true;
            return answer;
        }
        public Floor next() {
            if(mainOfficeFloori.last!=true){
            mainOfficeFloori=mainOfficeFloori.nextNote;
            return mainOfficeFloori.f;
            }else return null;
        }
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
    
    private int howMuchFloor() {
        note of = mainOfficeFloor;
        int n = 0;
        try {
            boolean exit = false;
            while (exit == false) {
                if (of.last == true) {
                    exit = true;
                }
                of = of.nextNote;
                n++;
                if (of == mainOfficeFloor && exit != true) {
                    throw new FloorIndexOutOfBoundsException();
                }
            }
        } catch (FloorIndexOutOfBoundsException e) {
            System.out.println(e);
        }
        return n;
    }
    private note getFloorPrivate(int n) {
        try {
            if (n < 0) {
                throw new FloorIndexOutOfBoundsException();
            }
            if (n >= 0) {
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                n %= howMuchFloor();
                if (n == 0) {
                    n = howMuchFloor();//деление произошло без остатка
                }
                note of = mainOfficeFloor;
                for (int i = 1; i < n; i++) {//обозначение офисов
                    of = of.nextNote;
                }
                return of;
            } else {
                return null;
            }
        } catch (FloorIndexOutOfBoundsException e) {
            System.out.println(e);
            return null;
        }
    }
    private void addFloor(note s, int n) {
        try {
            if (n < 0) {
                throw new FloorIndexOutOfBoundsException();
            }
            if (n >= 0) {
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                n %= howMuchFloor();
                if (n == 0) {
                    n = howMuchFloor();
                }

                if (n == 1) {//добавление в голову
                    mainOfficeFloor.prevNote.nextNote = s;
                    s.prevNote = mainOfficeFloor.prevNote;
                    s.nextNote = mainOfficeFloor;
                    mainOfficeFloor.prevNote = s;
                    mainOfficeFloor = s;
                    return;
                }
                if (n == howMuchFloor()) {//добавление в хвост
                    mainOfficeFloor.prevNote.last = false;
                    mainOfficeFloor.prevNote.nextNote = s;
                    s.last = true;
                    s.nextNote = mainOfficeFloor;
                    return;
                }
                if (n > 1 && n < howMuchFloor()) {//добавление в тело
                    note k, l;
                    n--;//костыль
                    k = getFloorPrivate(n - 1);
                    l = getFloorPrivate(n + 1);
                    k.nextNote = s;
                    s.prevNote = k;
                    s.nextNote = l;
                    l.prevNote = s;
                }
            }
        } catch (FloorIndexOutOfBoundsException e) {
            System.out.println(e);
        }
    }
    private void delFloor(int n) {
        try {
            if (n < 0) {
                throw new FloorIndexOutOfBoundsException();
            }
            if (n >= 0) {
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                n %= howMuchFloor();
                if (n == 0) {
                    n = howMuchFloor();
                }

                if (n == 1) {//удаление головы
                    note k;
                    k = mainOfficeFloor.prevNote;
                    mainOfficeFloor = mainOfficeFloor.nextNote;
                    mainOfficeFloor.prevNote = k;
                    mainOfficeFloor.prevNote.nextNote = mainOfficeFloor;
                    return;
                }
                if (n == howMuchFloor()) {//удаление хвоста
                    note k;
                    n--;//cost
                    k = getFloorPrivate(n - 1);
                    k.nextNote = mainOfficeFloor;
                    mainOfficeFloor.prevNote = k;
                    k.last = true;
                    return;
                }
                if (n > 1 && n < howMuchFloor()) {//удаления в теле
                    note k, l;
                    n--;//cost
                    k = getFloorPrivate(n - 1);
                    l = getFloorPrivate(n + 1);
                    k.nextNote = l;
                    l.prevNote = k;
                }
            }
        } catch (FloorIndexOutOfBoundsException e) {
            System.out.println(e);
        }
//        try{
//        if(n<=0)throw new FloorIndexOutOfBoundsException();
//        if(n>0){
//            n%=howMuchFloor();
//            if(n==0)n=howMuchFloor();
//            
//            if(n==1){//удаление головы
//                OfficeFloor k,l;
//                k=mainOfficeFloor.prevFloor;
//                mainOfficeFloor=mainOfficeFloor.nextFloor;
//                mainOfficeFloor.prevFloor=k;
//                mainOfficeFloor.prevFloor.nextFloor=mainOfficeFloor;
//                return;
//            }
//            if(n==howMuchFloor()){//удаление хвоста
//                OfficeFloor k;
//                k=getFloorPrivate(n-1);
//                k.nextFloor=mainOfficeFloor;
//                mainOfficeFloor.prevFloor=k;
//                return;
//            }
//            if(n>1 && n<howMuchFloor()){//удаления в теле
//                OfficeFloor k,l;
//                k=getFloorPrivate(n-1);
//                l=getFloorPrivate(n+1);
//                k.nextFloor=l;
//                l.prevFloor=k;
//            }
//        }
//        }catch(FloorIndexOutOfBoundsException e){
//            System.out.println(e);
//        }
    }
    
    @Override
    public java.util.Iterator iterator() {
        return new myIterator();
    }
    @Override
    public int getHowMuchFloorInBuilding() {
        return howMuchFloor();
    }
    @Override
    public int getHowMuchSpaceInBuilding() {
        int nOffice;
        note of = mainOfficeFloor;
        nOffice = of.f.getHowMuchSpaceInFloor();
        for (int i = 1; i < howMuchFloor(); i++) {
            of = of.nextNote;
            nOffice += of.f.getHowMuchSpaceInFloor();
        }
        return nOffice;
//        int nOffice;
//        OfficeFloor of=mainOfficeFloor;
//        nOffice=of.getHowMuchSpaceInFloor();
//        for(int i=1;i<howMuchFloor();i++){
//            of=of.nextFloor;
//            nOffice+=of.getHowMuchSpaceInFloor();
//        }
//        return nOffice;
    }
    @Override
    public double getHowMuchAreaInBuilding() {
        double s = 0;
        note of = mainOfficeFloor;
        s = of.f.getHowMuchAreaInFloor();
        for (int i = 1; i < howMuchFloor(); i++) {
            of = of.nextNote;
            s += of.f.getHowMuchAreaInFloor();
        }
        return s;
//        double s=0;
//        OfficeFloor of=mainOfficeFloor;
//        s=of.getHowMuchAreaInFloor();
//        for(int i=1;i<howMuchFloor();i++){
//            of=of.nextFloor;
//            s+=of.getHowMuchAreaInFloor();
//        }
//        return s;
    }
    @Override
    public int getHowMuchRoomsInBuilding() {
        int k;
        note of = mainOfficeFloor;
        k = of.f.getHowMuchRoomsInFloor();
        for (int i = 1; i < howMuchFloor(); i++) {
            of = of.nextNote;
            k += of.f.getHowMuchRoomsInFloor();
        }
        return k;
//        int k=0;
//        OfficeFloor of=mainOfficeFloor;
//        k=of.getHowMuchRoomsInFloor();
//        for(int i=1;i<howMuchFloor();i++){
//            of=of.nextFloor;
//            k+=of.getHowMuchRoomsInFloor();
//        }
//        return k;
    }
    @Override
    public Floor[] getFloorMass() {//отдаем новый массив где элементы ссылки на этажи
        Floor mass[];
        int n = howMuchFloor();
        mass = new Floor[n];
        note of = mainOfficeFloor;
        for (int i = 0; i < n; i++) {
            mass[i] = of.f;
            if (i == (n - 1)) {
                break;
            }
            of = of.nextNote;
        }
        return mass;
//        OfficeFloor mass[];
//        int n=howMuchFloor();
//        mass=new OfficeFloor[n];
//        OfficeFloor of=mainOfficeFloor;
//        for(int i=0;i<n;i++){
//            mass[i]=new OfficeFloor(of.getMassSpacesInFloor());
//            if(i==(n-1))break;
//            of=of.nextFloor;
//        }
//        return mass;
    }
    @Override
    public Floor getFloor(int n) {
        note s = getFloorPrivate(n);
        return s.f;
    }
    @Override
    public void setFloor(int n, Floor s) {
        try {
            if (n < 0) {
                throw new FloorIndexOutOfBoundsException();
            }
            if (n >= 0) {
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                //note x=new note(s);
                note x = new note((Floor) s.clone());
                int m = howMuchFloor();
                n %= m;
                if (n == 0) {
                    n = howMuchFloor();
                }
                if (n == 1) {
                    note p = mainOfficeFloor.prevNote;
                    note ne = mainOfficeFloor.nextNote;
                    mainOfficeFloor = x;
                    mainOfficeFloor.prevNote = p;
                    p.nextNote = mainOfficeFloor;
                    mainOfficeFloor.nextNote = ne;
                    ne.prevNote = mainOfficeFloor;
                    return;
                }
                if (n == m) {
                    note p = mainOfficeFloor.prevNote.prevNote;
                    p.nextNote = x;
                    x.last = true;
                    mainOfficeFloor.prevNote = x;
                    return;
                }
                if (n > 1 && n < m) {
                    n--;//cost
                    note p = getFloorPrivate(n - 1);
                    note ne = getFloorPrivate(n + 1);
                    p.nextNote = x;
                    x.prevNote = p;
                    x.nextNote = ne;
                    ne.prevNote = x;
                    return;
                }
            }
        } catch (FloorIndexOutOfBoundsException e) {
            System.out.println(e);
        }
//        try{
//        if(n<=0)throw new FloorIndexOutOfBoundsException();
//        delFloor(n);
//        addFloor(s,n);
//        }catch(FloorIndexOutOfBoundsException e){
//            System.out.println(e);
//        }
    }
    @Override
    public Space getSpaceInBuilding(int n) {
        Space result = null;
        boolean found = false;
        //костыль для нумерации с нуля
        n++;
        //конец костыля
        if (n > 0) {
            note of = mainOfficeFloor;
            for (int i = 0; i < howMuchFloor(); i++) {
                if (n <= of.f.getHowMuchSpaceInFloor()) {
                    n--;//cost
                    result = of.f.getSpaceInFloor(n);
                    found = true;
                    return result;
                }
                if (n > of.f.getHowMuchSpaceInFloor()) {
                    n -= of.f.getHowMuchSpaceInFloor();
                    of = of.nextNote;
                }
            }
            if (found == false) {
                System.out.println("Не нашли такой офис с таким номером");
            }
        }
        return result;
//        Office result=null;
//        boolean found=false;
//        if(n>0){
//            OfficeFloor of=mainOfficeFloor;
//            for(int i=0;i<howMuchFloor();i++){
//                if(n<=of.getHowMuchSpaceInFloor()){
//                    result=of.getSpaceInFloor(n);
//                    found=true;
//                }
//                if(n>of.getHowMuchSpaceInFloor()){
//                    n-=of.getHowMuchSpaceInFloor();
//                    of=of.nextFloor;
//                }
//            }
//            if(found==false)System.out.println("Не нашли такой офис с таким номером");
//        }
//        return result;
    }
    @Override
    public void setSpaceInBuilding(int n, Space s) {
        try {
            //костыль для нумерации с нуля
            n++;
            //конец костыля
            if (n > 0 && n <= getHowMuchSpaceInBuilding()) {
                //n--;//cost
                note of = mainOfficeFloor;
                boolean result = false;
                for (int i = 0; i < howMuchFloor(); i++) {
                    if (n <= of.f.getHowMuchSpaceInFloor()) {
                        n--;//cost
                        of.f.setSpaceInFloor(n, s);
                        result = true;
                        return;
                    }
                    if (n > of.f.getHowMuchSpaceInFloor()) {
                        n -= of.f.getHowMuchSpaceInFloor();
                        of = of.nextNote;
                    }
                }
                if (result == false) {
                    System.out.println("Такого помещения с таким номером нет");
                }
//            getSpaceInBuilding(n).setArea(s.getHowMuchArea());
//            getSpaceInBuilding(n).setRooms(s.getHowMuchRooms());
            } else {
                throw new SpaceIndexOutOfBoundsException();//System.out.println("Такого офиса нет");
            }
        } catch (SpaceIndexOutOfBoundsException e) {
            System.out.println(e);
        }
//        try{
//        if(n>0 && n<howMuchOffice()){
//        getOfficeOnBuilding(n).setArea(s.getHowMuchArea());
//        getOfficeOnBuilding(n).setRooms(s.getHowMuchRooms());
//        }else throw new SpaceIndexOutOfBoundsException();//System.out.println("Такого офиса нет");
//        }catch(SpaceIndexOutOfBoundsException e){
//            System.out.println(e);
//        }
    }
    @Override
    public void addSpaceInBuilding(int n, Space s) {
        try {
            if (n < 0) {
                throw new SpaceIndexOutOfBoundsException();
            }
            if (n >= 0) {
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                note of = mainOfficeFloor;
                for (int i = 0; i < howMuchFloor(); i++) {
                    if (n <= of.f.getHowMuchSpaceInFloor()) {
                        n--;//cost
                        of.f.addSpaceInFloor(n, s);
                        return;
                    }
                    if (n > of.f.getHowMuchSpaceInFloor()) {
                        if (i == (howMuchFloor() - 1)) {//на последнем шаге новый офис добавляем в самый конец последнего этажа
                            of.f.addSpaceInFloor(of.f.getHowMuchSpaceInFloor() - 1, s);
                            return;
                        }
                        n -= of.f.getHowMuchSpaceInFloor();
                        of = of.nextNote;
                    }
                }
            } else {
                throw new SpaceIndexOutOfBoundsException();
            }
        } catch (SpaceIndexOutOfBoundsException e) {
            System.out.println(e);
        }
//        try{
//        if(n>0){
//            OfficeFloor of=mainOfficeFloor;
//            for(int i=0;i<howMuchFloor();i++){
//                if(n<=of.getHowMuchSpaceInFloor()){
//                    of.addSpaceInFloor(n,s);
//                    return;
//                }
//                if(n>of.getHowMuchSpaceInFloor()){
//                   if(i==(howMuchFloor()-1)){//на последнем шаге новый офис добавляем в самый конец последнего этажа
//                      of.addSpaceInFloor(of.getHowMuchSpaceInFloor(), s);
//                      return;
//                   }
//                    n-=of.getHowMuchSpaceInFloor();
//                    of=of.nextFloor;
//                }
//            }
//    }else throw new SpaceIndexOutOfBoundsException();
//        }catch(SpaceIndexOutOfBoundsException e){
//            System.out.println(e);
//        }
    }
    @Override
    public void delSpaceInBuilding(int n) {
        boolean found = false;
        try {
            if (n < 0) {
                throw new SpaceIndexOutOfBoundsException();
            }
            if (n >= 0) {
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                note of = mainOfficeFloor;
                for (int i = 0; i < howMuchFloor(); i++) {
                    if (n <= of.f.getHowMuchSpaceInFloor()) {
                        n--;//cost
                        of.f.delSpaceInFloor(n);
                        found = true;
                        return;
                    }
                    if (n > of.f.getHowMuchSpaceInFloor()) {
                        n -= of.f.getHowMuchSpaceInFloor();
                        of = of.nextNote;//ничего страшного, все связано
                    }
                }
                if (found == false) {
                    System.out.println("Не нашли такой офис с таким номером");
                }
            } else {
                throw new SpaceIndexOutOfBoundsException();
            }
        } catch (SpaceIndexOutOfBoundsException e) {
            System.out.println(e);
        }
//        boolean found=false;
//        try{
//        if(n>0){
//            OfficeFloor of=mainOfficeFloor;
//            for(int i=0;i<howMuchFloor();i++){
//                if(n<=of.getHowMuchSpaceInFloor()){
//                    of.delSpaceInFloor(n);
//                    found=true;
//                }
//                if(n>of.getHowMuchSpaceInFloor()){
//                    n-=of.getHowMuchSpaceInFloor();
//                    of=of.nextFloor;//ничего страшного, все связано
//                }
//            }
//            if(found==false)System.out.println("Не нашли такой офис с таким номером");
//        }else throw new SpaceIndexOutOfBoundsException();
//        }catch(SpaceIndexOutOfBoundsException e){
//            System.out.println(e);
//        }
    }
    @Override
    public Space getBestSpace() {
        note of = mainOfficeFloor;
        Space Best = of.f.getBestSpace();
        for (int i = 0; i < howMuchFloor(); i++) {
            of = of.nextNote;
            double x, y;
            x = Best.getHowMuchArea();
            y = of.f.getBestSpace().getHowMuchArea();
            if (x < y) {
                Best = of.f.getBestSpace();
            }
        }
        return Best;
    }
    @Override
    public Space[] getSorted() {
        note of = mainOfficeFloor;
        int n = 0;
        for (int i = 0; i < howMuchFloor(); i++) {
            n += of.f.getMassSpacesInFloor().length;
            of = of.nextNote;
        }
        Space mass[] = new Space[n];
        //сбор данных для создания массива нужной длинны

        of = mainOfficeFloor;
        int k = 0;
        for (int i = 0; i < howMuchFloor(); i++) {
            Space massh[] = of.f.getMassSpacesInFloor();
            for (int j = 0; j < massh.length; j++) {
                mass[k] = massh[j];
                k++;
            }
            of = of.nextNote;
        }
        //заполнение массива

        //сортировка шелла
        int shag;
        Space buf;
        shag = (int) n / 2;
        while (shag > 0) {
            for (int i = 0; i < (n - shag); i++) {
                if (mass[i].getHowMuchArea() < mass[i + shag].getHowMuchArea()) {
                    buf = mass[i];
                    mass[i] = mass[i + shag];
                    mass[i + shag] = buf;
                }
            }
            shag--;
        }
        return mass;
//        note of=mainOfficeFloor;
//        int n=0;
//        for(int i=0;i<howMuchFloor();i++){
//            n+=of.f.getMassSpacesInFloor().length;
//            of=of.nextNote;
//        }
//        Space mass[]=new Office[n];
//        //сбор данных для создания массива нужной длинны
//        
//        of=mainOfficeFloor;
//        int k=0;
//        for(int i=0;i<howMuchFloor();i++){
//            Office massh[]=of.getMassSpacesInFloor();
//            for(int j=0;j<massh.length;j++){
//                mass[k]=massh[j];
//                k++;
//            }
//            of=of.nextFloor;
//        }
//        //заполнение массива
//        
//        //сортировка шелла
//        int shag;
//        Office buf;
//        shag=(int)n/2;
//        while(shag>0){
//            for(int i=0;i<(n-shag);i++){
//                if(mass[i].getHowMuchArea()<mass[i+shag].getHowMuchArea()){
//                    buf=mass[i];
//                    mass[i]=mass[i+shag];
//                    mass[i+shag]=buf;   
//                }
//            }
//            shag--;
//        }
//        return mass;
    }

    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append(("Офиное здание \n"));
        for (int i = 0; i < getHowMuchFloorInBuilding(); i++) {
            s.append((i + getFloor(i).toString()));
        }
        return s.toString();
    }
    public boolean equals(Object o) {
        boolean answer = false;
        if (o.getClass() == this.getClass()) {
            if (((OfficeBuilding) o).getHowMuchFloorInBuilding() == this.getHowMuchFloorInBuilding()) {
                boolean key = true;
                for (int i = 0; i < getHowMuchFloorInBuilding(); i++) {
                    if (this.getFloor(i).equals(((OfficeBuilding) o).getFloor(i)) == false) {
                        key = false;
                        break;
                    }
                }
                if (key == true) {
                    answer = true;
                }
            }
        }
        return answer;
    }
    public int hashCode() {
        int hash;
        hash = (int) (Integer.bitCount(this.getHowMuchFloorInBuilding()));
        for (int i = 0; i < this.getHowMuchFloorInBuilding(); i++) {
            hash = (int) (hash ^ this.getFloor(i).hashCode());
        }
        return hash;
    }
    public Object clone() {
        Floor[] forClone=new Floor[getHowMuchFloorInBuilding()];
        note of = mainOfficeFloor;
        for(int i=0;i<getHowMuchFloorInBuilding();i++){
            forClone[i]=(Floor)of.f.clone();
            of=of.nextNote;
        }
        return new OfficeBuilding(forClone);
    }

    public OfficeBuilding(int n, int ... mass) {
        try {
            if ((n > 0) && (n == (mass.length))) {
                mainOfficeFloor = new note(new OfficeFloor(mass[0]));
                note of = mainOfficeFloor;
                for (int i = 1; i < n; i++) {
                    of.nextNote = new note(new OfficeFloor(mass[i]));
                    of.nextNote.prevNote = of;
                    if (i == (n - 1)) {
                        of.nextNote.last = true;
                        of.nextNote.nextNote = mainOfficeFloor;
                        of.nextNote.prevNote = of;
                        mainOfficeFloor.prevNote = of.nextNote;
                        break;
                    }
                    of = of.nextNote;
                }
            } else {
                throw new FloorIndexOutOfBoundsException();
            }
        } catch (FloorIndexOutOfBoundsException e) {
            System.out.println(e);
        }
//        if((n>0) && (n==(mass.length))){
//            OfficeFloor of=mainOfficeFloor=new OfficeFloor(mass[0]);
//            for(int i=1;i<n;i++){
//                of.nextFloor=new OfficeFloor(mass[i]);
//                of.nextFloor.prevFloor=of;
//                if(i==(n-1)){
//                    of.nextFloor.last=true;
//                    break;
//                }
//                of=of.nextFloor;
//            }
//            of=getFloorOnBuilding(n);
//            of.nextFloor=mainOfficeFloor;
//            mainOfficeFloor.prevFloor=of;
//        }
    }
    public OfficeBuilding(Floor ... mass) {//создаем на принятом этаже
        int n = mass.length;
        try {
            if (n > 0) {
                mainOfficeFloor = new note((Floor) mass[0]);
                note of = mainOfficeFloor;
                for (int i = 1; i < n; i++) {
                    of.nextNote = new note((Floor) mass[i]);
                    of.nextNote.prevNote = of;
                    if (i == (n - 1)) {
                        of.nextNote.last = true;
                        of.nextNote.nextNote = mainOfficeFloor;
                        of.nextNote.prevNote = of;
                        mainOfficeFloor.prevNote = of.nextNote;
                        break;
                    }
                    of = of.nextNote;
                }
            } else {
                throw new FloorIndexOutOfBoundsException();
            }
        } catch (FloorIndexOutOfBoundsException e) {
            System.out.println(e);
        }
    }
}
